#ifndef PACKBUFFER_H
#define PACKBUFFER_H

#include <assert.h>
#include "windows.h"

namespace PackIt {

	class CPackBuffer;
	class CDepacker;


	//////////////////////////////////////////////////////////////////////////
	// Any class that we want to add support for CPackBuffer and CDepacker can 
	// implement two following methods:
	//
	//  1. CPackBuffer ToPackBuffer() const;
	//      pack the data of the object to a object of CPackBuffer, and return 
	//		this object
	//
	//  2. bool Depack(CDepacker& dp);
	//      depack the data of the object from a CDepacker
	//
	//
	//  then we can pack this class object conveniently by using the following code:
	//
	// ---------------------------------------------------------------------
	//  sample code for pack the object:
	//
	//    CPackBuffer packbuffer;
	//    CSomeClass object;
	//    packbuffer.Pack(object);
	//
	//  smaple code for depack the object:
	//
	//    CDepacker depacker(databuffer, bufferlength);
	//    CSomeClass object;
	//    depacker.Read(object);
	//
	//////////////////////////////////////////////////////////////////////////


	//////////////////////////////////////////////////////////////////////////
	// struct PackBufferOut
	//

	struct PackBufferIn
	{
		PackBufferIn(const void* pBuffer, size_t length) : m_pBuffer(pBuffer), m_nLength(length){}

		const void* m_pBuffer;
		size_t m_nLength;
	};

	template<typename T>
	struct PackBufferArrayIn
	{
		explicit PackBufferArrayIn(const T* pArray, size_t size): m_pVal(pArray), m_size(size){	}

		const T*	m_pVal;
		size_t m_size;
	};

	//////////////////////////////////////////////////////////////////////////u
	// struct PackBufferIn
	//

	struct PackBufferOut
	{
		PackBufferOut(): m_pBuffer(NULL), m_nLength(0) {}
		~PackBufferOut()
		{
			if(m_pBuffer != NULL){
				delete[] m_pBuffer;
			}
		}
		void* Detach() 
		{ 
			void* pBuffer= m_pBuffer;
			m_pBuffer = NULL;
			return pBuffer;
		}
		void* m_pBuffer;
		size_t m_nLength;
	};

	template<typename T>
	struct PackBufferArrayOut
	{
		PackBufferArrayOut(T* pArray, size_t size): m_pArray(pArray), m_uSize(size) {}

		T* m_pArray;
		size_t m_uSize;
	};
	//////////////////////////////////////////////////////////////////////////
	// class CPack Buffer
	//

#define PACK_PRIMITIVE_VALUE(val)		\
	{										\
	size_t nRetSize = 0;				\
	if(ReallocateBuffer(sizeof(val)))	\
	{									\
	nRetSize = FastPack(val);		\
	}									\
	return nRetSize;					\
	}

#define FASTPACK_PRIMITIVE_VALUE(val)		\
	{											\
	size_t nRetSize = sizeof(val);			\
	memcpy_s(m_pBuffer + m_nUsedLength, nRetSize, &val, nRetSize);		\
	m_nUsedLength += nRetSize;											\
	return nRetSize;													\
	}

	class CPackBuffer{
	public:
		CPackBuffer()
		{
			m_nAllocLength = INIT_BUFFER_LENGTH;
			m_pBuffer = new BYTE[m_nAllocLength];
			m_nUsedLength = 0;
		}

		explicit CPackBuffer(size_t nInitBufLength)
		{
			m_nAllocLength = nInitBufLength;
			m_pBuffer = new BYTE[m_nAllocLength];
			m_nUsedLength = 0;
		}

		CPackBuffer(const CPackBuffer& src)				
		{
			CPackBuffer& pack = const_cast<CPackBuffer&>(src);
			m_nAllocLength = pack.m_nAllocLength;
			m_nUsedLength = pack.m_nUsedLength;
			m_pBuffer = static_cast<BYTE*>(pack.Detach());
		}

		~CPackBuffer()
		{
			Free();
		}

	public:
		/*
		* Pack methods
		* return: 0 if failed, otherwise return the packed data length
		*/

		size_t Pack(bool val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(char val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(unsigned char val)		{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(short val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(unsigned short val)		{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(int val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(unsigned int val)		{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(long val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(unsigned long val)		{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(long long val)			{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(unsigned long long val)	{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(float val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(double val)				{ PACK_PRIMITIVE_VALUE(val); }
		size_t Pack(long double val)		{ PACK_PRIMITIVE_VALUE(val); }


		//for class types that implement two required methods for CPackBuffer and CDepacker
		//template<typename T> size_t Pack(const T* pVal)
		//{
		//	return Pack(pVal->ToPackBuffer());
		//}
		template<typename T>
		class TypeTraits
		{
		private:
			template <class U> struct PointerTraits
			{
				enum { result = false};
			};
			template <class U> struct PointerTraits<U*>
			{
				enum { result = true};
			};
		public:
			enum { isPointer = PointerTraits<T>::result};
		};

		template<bool T>
		struct Bool2Type
		{
			enum { type = true};
		};

		template<> 
		struct Bool2Type<false>
		{
			enum { type = false};
		};
		template<typename T> size_t Pack(const T& val)
		{
			return PackPointer(val,Bool2Type<TypeTraits<T>::isPointer>());
		}

		template<typename T> size_t PackPointer(const T& val, Bool2Type<true>)
		{
			return Pack(val->ToPackBuffer());
		}

		template<typename T> size_t PackPointer(const T& val, Bool2Type<false>)
		{
			return Pack(val.ToPackBuffer());
		}

		size_t Pack(const CPackBuffer& pack)
		{
			size_t nRetSize = 0;
			if(ReallocateBuffer(GetTypeSize(pack)))
			{
				nRetSize = FastPack(pack);
			}
			return nRetSize;

		}

		////for strings
		size_t Pack(const char* str);
		size_t Pack(char* str);		//implement this method to avoid array of char enter Pack(const T& val)
		size_t Pack(const wchar_t* str);
		size_t Pack(wchar_t* str);	//implement this method to avoid array of wchar_t enter Pack(const T& val)

		//for common data
		size_t Pack(const PackBufferIn packBuffer);

		//for array
		template<typename T>
		size_t Pack(const PackBufferArrayIn<T> pbai);

		//we do not implement Pack(const T*) and Pack(T*), any call to these methods will get a link error
		//except ones that implemented above
		//	template<typename T> size_t Pack(const T* val);
		//	template<typename T> size_t Pack(T* val);

		/*
		*	template methods for multi-parameter Pack
		*/
		template <typename P1, typename P2> 
		size_t  Pack(const P1& v1, const P2& v2);

		template <typename P1, typename P2, typename P3> 
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3);

		template <typename P1, typename P2, typename P3, typename P4> 
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4);

		template <typename P1, typename P2, typename P3, typename P4, typename P5> 
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> 
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> 
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8, const P9& v9);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10>
		size_t  Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8, const P9& v9, const P10& v10);


		size_t GetDataLength() const { return m_nUsedLength; }
		const void* GetData() const { return m_pBuffer; }


	public:
		/*template<typename T>
		static size_t GetTypeSize(const T* pVal)
		{
			return GetTypeSize(pVal->ToPackBuffer());
		}*/
		template<typename T> 
		static size_t GetTypeSizeP(const T& val, Bool2Type<true>)
		{
			return GetTypeSize(val->ToPackBuffer());
		}

		template<typename T> 
		static size_t GetTypeSizeP(const T& val, Bool2Type<false>)
		{
			return GetTypeSize(val.ToPackBuffer());
		}

		template<typename T>
		static size_t GetTypeSize(const T& val)
		{
			return GetTypeSizeP(val, Bool2Type<TypeTraits<T>::isPointer>());
		}

		static size_t GetTypeSize(const bool val)				{ return sizeof(val); }
		static size_t GetTypeSize(const char val)				{ return sizeof(val); }
		static size_t GetTypeSize(const unsigned char val)		{ return sizeof(val); }
		static size_t GetTypeSize(const short val)				{ return sizeof(val); }
		static size_t GetTypeSize(const unsigned short val)		{ return sizeof(val); }
		static size_t GetTypeSize(const int val)				{ return sizeof(val); }
		static size_t GetTypeSize(const unsigned int val)		{ return sizeof(val); }
		static size_t GetTypeSize(const long val)				{ return sizeof(val); }
		static size_t GetTypeSize(const unsigned long val)		{ return sizeof(val); }
		static size_t GetTypeSize(const long long val)			{ return sizeof(val); }
		static size_t GetTypeSize(const unsigned long long val)	{ return sizeof(val); }
		static size_t GetTypeSize(const float val)				{ return sizeof(val); }
		static size_t GetTypeSize(const double val)				{ return sizeof(val); }
		static size_t GetTypeSize(const long double val)		{ return sizeof(val); }

		static size_t GetTypeSize(const PackBufferIn packBuffer) { return packBuffer.m_nLength + sizeof(packBuffer.m_nLength);}

		//for array
		template<typename T>
		static size_t GetTypeSize(PackBufferArrayIn<T> pbai)
		{
			//		return GetTypeSize(pbai.m_pVal[0]) * pbai.m_size + sizeof(pbai.m_size); 
			return GetTypeSize(pbai.m_pVal[0]) * pbai.m_size; 
		}

		//for string
		static size_t GetTypeSize(const char* str)
		{
			assert(str);
			size_t length = strlen(str);
			return length + sizeof(length);
		}
		static size_t GetTypeSize(char* str)		//implement this to avoid array of char enter GetTypeSize(const T& val)
		{
			return GetTypeSize(static_cast<const char*>(str));
		}
		static size_t GetTypeSize(const wchar_t* str)
		{
			assert(str);
			size_t length = wcslen(str) * sizeof(wchar_t);
			return length + sizeof(length);
		}
		static size_t GetTypeSize(wchar_t* str)	//implement this to avoid array of wchar_t enter GetTypeSize(const T& val)
		{
			return GetTypeSize(static_cast<const wchar_t*>(str));
		}

		static size_t GetTypeSize(const CPackBuffer& pack)
		{
			return GetTypeSize(PackBufferIn(pack.GetData(), pack.GetDataLength()));
		}

	private:
		CPackBuffer& operator = (const CPackBuffer& rhs);	//no assign available

		void* ReallocateBuffer(size_t nNewAddedSize);	//check the buffer size and allocate new buffer if buffer size is not enough
		void* Detach()
		{
			void* pRetBuffer = m_pBuffer;
			m_nUsedLength = 0;
			m_nAllocLength = 0;
			m_pBuffer = NULL;
			return pRetBuffer;
		}

		//for class types that implement three required methods for CPackBuffer and CDepacker
		//this method do not check the available memory, make sure there is enough buffer space left
		
		/*template<typename T> size_t FastPack(const T* pVal)
		{
			return FastPack((pVal->ToPackBuffer()));
		}*/
		template<typename T> size_t FastPackP(const T& val, Bool2Type<true>)
		{
			return FastPack(val->ToPackBuffer());
		}

		template<typename T> size_t FastPackP(const T& val, Bool2Type<false>)
		{
			return FastPack(val.ToPackBuffer());
		}
		template<typename T> size_t FastPack(const T& val)
		{
			return FastPackP(val, Bool2Type<TypeTraits<T>::isPointer>());
		}

		size_t FastPack(const bool val)					{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const char val)					{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const unsigned char val)		{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const short val)				{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const unsigned short val)		{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const int val)					{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const unsigned int val)			{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const long val)					{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const unsigned long val)		{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const long long val)			{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const unsigned long long val)	{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const float val)				{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const double val)				{ FASTPACK_PRIMITIVE_VALUE(val); }
		size_t FastPack(const long double val)			{ FASTPACK_PRIMITIVE_VALUE(val); }

		//for strings
		size_t FastPack(const char* str);
		size_t FastPack(char* str);		//implement this to avoid array of char enter FastPack(const T& val)
		size_t FastPack(const wchar_t* str);
		size_t FastPack(wchar_t* str);	//implement this to avoid array of wchar_t enter FastPack(const T& val)

		//for common data
		size_t FastPack(const PackBufferIn packBuffer);

		size_t FastPack(const CPackBuffer& pack)
		{
			return FastPack(PackBufferIn(pack.GetData(), pack.GetDataLength()));
		}

		template<typename T>
		size_t FastPack(PackBufferArrayIn<T> pbai);

		//for array
		template<typename T>
		size_t FastPack(T t[], int n);


	private:
		void Free()
		{
			if (m_pBuffer)
			{
				delete[] m_pBuffer;
				m_pBuffer = NULL;
			}
			m_nAllocLength = 0;
			m_nUsedLength = 0;
		}

	private:
		enum { INIT_BUFFER_LENGTH = 64 };
		BYTE* m_pBuffer;
		size_t m_nAllocLength;
		size_t m_nUsedLength;
	};

	inline void* CPackBuffer::ReallocateBuffer(size_t nNewAddedSize)
	{
		BYTE* pRet = NULL;
		size_t nNewUsedLength = m_nUsedLength + nNewAddedSize;

		if(nNewUsedLength > m_nAllocLength)
		{
			//no enough size, do realloc
			do{
				m_nAllocLength = m_nAllocLength * 2;
			}while(m_nAllocLength < nNewUsedLength);


			BYTE* pNewBuffer =  new BYTE[m_nAllocLength];
			if ( pNewBuffer!=NULL)
			{
				memcpy_s(pNewBuffer, m_nUsedLength, m_pBuffer, m_nUsedLength);
				delete[] m_pBuffer;
				pRet = m_pBuffer = pNewBuffer;
				//			cout<<"Realloc new buffer, size is "<<m_nAllocLength<<endl;
			}
		}
		else
		{
			pRet = m_pBuffer;
		}
		return pRet;
	}

	inline size_t CPackBuffer::Pack(char* str)
	{
		return Pack(static_cast<const char*>(str));
	}

	inline size_t CPackBuffer::Pack(const char* str)
	{
		assert(str);
		size_t datalen = strlen(str);
		return Pack(PackBufferIn(reinterpret_cast<const void*>(str), datalen));
	}

	inline size_t CPackBuffer::Pack(wchar_t* str)
	{
		return Pack(static_cast<const wchar_t*>(str));
	}

	inline size_t CPackBuffer::Pack(const wchar_t* str)
	{
		assert(str);
		size_t datalen = wcslen(str) * sizeof(wchar_t);
		return Pack(PackBufferIn(reinterpret_cast<const void*>(str), datalen));
	}

	inline size_t CPackBuffer::Pack(const PackBufferIn packBufferIn)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(sizeof(packBufferIn.m_nLength) + packBufferIn.m_nLength))
		{
			nRetSize = FastPack(packBufferIn);
		}
		return nRetSize;
	}

	template<typename T>
	inline size_t CPackBuffer::Pack(const PackBufferArrayIn<T> pbai)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(pbai)))
		{
			nRetSize = FastPack(pbai);	
		}
		return nRetSize;
	}

	inline size_t CPackBuffer::FastPack(char* str)
	{
		return FastPack(static_cast<const char*>(str));
	}

	inline size_t CPackBuffer::FastPack(const char* str)
	{
		assert(str);
		size_t datalen = strlen(str);
		return FastPack(PackBufferIn(reinterpret_cast<const void*>(str), datalen));
	}

	inline size_t CPackBuffer::FastPack(wchar_t* str)
	{
		return FastPack(static_cast<const wchar_t*>(str));
	}

	inline size_t CPackBuffer::FastPack(const wchar_t* str)
	{
		assert(str);
		size_t datalen = wcslen(str) * sizeof(wchar_t);
		return FastPack(PackBufferIn(reinterpret_cast<const void*>(str), datalen));
	}

	inline size_t CPackBuffer::FastPack(const PackBufferIn packBufferIn)
	{
		memcpy_s(m_pBuffer + m_nUsedLength, sizeof(packBufferIn.m_nLength), reinterpret_cast<const void*>(&packBufferIn.m_nLength), sizeof(packBufferIn.m_nLength));
		m_nUsedLength += sizeof(packBufferIn.m_nLength);
		memcpy_s(m_pBuffer + m_nUsedLength, packBufferIn.m_nLength, packBufferIn.m_pBuffer, packBufferIn.m_nLength);
		m_nUsedLength += packBufferIn.m_nLength;

		return sizeof(packBufferIn.m_nLength) + packBufferIn.m_nLength;
	}

	template<typename T>
	inline size_t CPackBuffer::FastPack(PackBufferArrayIn<T> pbai)
	{
		size_t uSize = 0;// FastPack((unsigned int)pbai.m_size);

		for(size_t i = 0; i<pbai.m_size; i++)
		{
			uSize += FastPack(pbai.m_pVal[i]);
		}
		return uSize;
	}


	template <typename P1, typename P2> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
		}
		return nRetSize;
	}

	template <typename P1, typename P2, typename P3> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
		}
		return nRetSize;
	}

	template <typename P1, typename P2, typename P3, typename P4> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
		}
		return nRetSize;
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
		}
		return nRetSize;
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5) + GetTypeSize(v6)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5) + FastPack(v6);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
			nRetSize += FastPack(v6);
		}
		return nRetSize;	
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> 
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5) + GetTypeSize(v6) + GetTypeSize(v7)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5) + FastPack(v6) + FastPack(v7);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
			nRetSize += FastPack(v6);
			nRetSize += FastPack(v7);
		}
		return nRetSize;	
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8>
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5) + GetTypeSize(v6) + GetTypeSize(v7) + GetTypeSize(v8)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5) + FastPack(v6) + FastPack(v7) + FastPack(v8);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
			nRetSize += FastPack(v6);
			nRetSize += FastPack(v7);
			nRetSize += FastPack(v8);
		}
		return nRetSize;	
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9>
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8, const P9& v9)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5) + GetTypeSize(v6) + GetTypeSize(v7) + GetTypeSize(v8) + GetTypeSize(v9)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5) + FastPack(v6) + FastPack(v7) + FastPack(v8) + FastPack(v9);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
			nRetSize += FastPack(v6);
			nRetSize += FastPack(v7);
			nRetSize += FastPack(v8);
			nRetSize += FastPack(v9);
		}
		return nRetSize;	
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10>
	inline size_t CPackBuffer::Pack(const P1& v1, const P2& v2, const P3& v3, const P4& v4, const P5& v5, const P6& v6, const P7& v7, const P8& v8, const P9& v9, const P10& v10)
	{
		size_t nRetSize = 0;
		if(ReallocateBuffer(GetTypeSize(v1) + GetTypeSize(v2) + GetTypeSize(v3) + GetTypeSize(v4) + GetTypeSize(v5) + GetTypeSize(v6) + GetTypeSize(v7) + GetTypeSize(v8) + GetTypeSize(v9) + GetTypeSize(v10)) != NULL)
		{		
			//		nRetSize = FastPack(v1) + FastPack(v2) + FastPack(v3) + FastPack(v4) + FastPack(v5) + FastPack(v6) + FastPack(v7) + FastPack(v8) + FastPack(v9) + FastPack(v10);
			nRetSize += FastPack(v1);
			nRetSize += FastPack(v2);
			nRetSize += FastPack(v3);
			nRetSize += FastPack(v4);
			nRetSize += FastPack(v5);
			nRetSize += FastPack(v6);
			nRetSize += FastPack(v7);
			nRetSize += FastPack(v8);
			nRetSize += FastPack(v9);
			nRetSize += FastPack(v10);
		}
		return nRetSize;	
	}

	//////////////////////////////////////////////////////////////////////////
	// class CDepacker
	//
#define READ_PRIMITIVE_VALUE(val)												\
	{																				\
	bool bResult = true;														\
	if(m_nCurrentPos + sizeof(val) > m_nBufferLen){								\
	bResult = false;														\
	}																			\
	if(bResult)																	\
	{																			\
	memcpy_s(&val, sizeof(val), m_pBuffer + m_nCurrentPos, sizeof(val));	\
	m_nCurrentPos += sizeof(val);											\
	}																			\
	return bResult;																\
	}

	class CDepacker
	{
	public:
		CDepacker(const void* pBuffer, size_t nBufferLen)
		{
			m_pBuffer = reinterpret_cast<const BYTE*>(pBuffer);
			m_nBufferLen = nBufferLen;
			m_nCurrentPos = 0;
		}

	public:
		/*
		* Read methods
		* return: ture if read success
		*/
		bool Read(bool& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(char& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(unsigned char& val)		{ READ_PRIMITIVE_VALUE(val); }
		bool Read(short& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(unsigned short& val)		{ READ_PRIMITIVE_VALUE(val); }
		bool Read(int& val)					{ READ_PRIMITIVE_VALUE(val); }
		bool Read(unsigned int& val)		{ READ_PRIMITIVE_VALUE(val); }
		bool Read(long& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(unsigned long& val)		{ READ_PRIMITIVE_VALUE(val); }
		bool Read(long long& val)			{ READ_PRIMITIVE_VALUE(val); }
		bool Read(unsigned long long& val)	{ READ_PRIMITIVE_VALUE(val); }
		bool Read(float& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(double& val)				{ READ_PRIMITIVE_VALUE(val); }
		bool Read(long double& val)			{ READ_PRIMITIVE_VALUE(val); }

		//for class types that implement two required methods for CPackBuffer and CDepacker
		template<typename T>
		class TypeTraits
		{
		private:
			template <class U> struct PointerTraits
			{
				enum { result = false};
			};
			template <class U> struct PointerTraits<U*>
			{
				enum { result = true};
			};
		public:
			enum { isPointer = PointerTraits<T>::result};
		};

		template<bool T>
		struct Bool2Type
		{
			enum { type = true};
		};

		template<> 
		struct Bool2Type<false>
		{
			enum { type = false};
		};
		
		template<typename T> bool ReadP(T& val, Bool2Type<true>)
		{
			bool bResult = true;
			PackBufferOut pbo;
			if(!Read(pbo))
			{
				bResult = false;
			}
			return bResult && val->Depack(CDepacker(pbo.m_pBuffer, pbo.m_nLength));
		}

		template<typename T> bool ReadP(T& val, Bool2Type<false>)
		{
			bool bResult = true;
			PackBufferOut pbo;
			if(!Read(pbo))
			{
				bResult = false;
			}
			return bResult && val.Depack(CDepacker(pbo.m_pBuffer, pbo.m_nLength));
		}

		template<typename T>
		bool Read(T& val)
		{
			return ReadP(val, Bool2Type<TypeTraits<T>::isPointer>());
		}

		//for string
		bool Read(char*& str);
		bool Read(wchar_t*& str);

		//for common data
		bool Read(PackBufferOut& packBuffer);

		//we do not implement Read(T*&), any call to this method will get a link error
		//except ones that implemented above
		//	template<typename T>
		//	size_t Read(T*& val);

		//for array
		template<typename T>
		bool Read(PackBufferArrayOut<T>& pbao);

		//template functions
		template <typename P1, typename P2> 
		bool  Read(P1& v1, P2& v2);

		template <typename P1, typename P2, typename P3> 
		bool  Read(P1& v1, P2& v2, P3& v3);

		template <typename P1, typename P2, typename P3, typename P4> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4);

		template <typename P1, typename P2, typename P3, typename P4, typename P5> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8, P9& v9);

		template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> 
		bool  Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8, P9& v9, P10& v10);

	private:
		const BYTE* m_pBuffer;
		size_t m_nBufferLen;
		size_t m_nCurrentPos;

	};

	inline bool CDepacker::Read(char*& str)
	{
		bool bResult = true;
		//length
		size_t datalen;
		if(!Read(datalen))
		{
			bResult = false;
		}

		//data
		if(bResult && m_nCurrentPos + datalen > m_nBufferLen)
		{
			bResult = false;
		}	
		if(bResult)
		{
			str = new char[datalen + 1];
			memcpy_s(str, datalen, m_pBuffer + m_nCurrentPos, datalen);
			str[datalen] = 0;
			m_nCurrentPos += datalen;
		}

		return bResult;
	}

	inline bool CDepacker::Read(wchar_t*& str)
	{
		bool bReault = true;
		//length
		size_t datalen;
		if(!Read(datalen))
		{
			bReault = false;
		}

		//data
		if(bReault && m_nCurrentPos + datalen > m_nBufferLen){
			bReault = false;
		}
		if(bReault)
		{
			size_t str_len = datalen/sizeof(wchar_t);

			str = new wchar_t[str_len + 1];
			memcpy_s(str, datalen, m_pBuffer + m_nCurrentPos, datalen);
			str[str_len] = 0;
			m_nCurrentPos += datalen;
		}
		return bReault;
	}

	inline bool CDepacker::Read(PackBufferOut& packBuffer)
	{
		bool bResult = true;

		if(packBuffer.m_pBuffer){
			delete packBuffer.m_pBuffer;
			packBuffer.m_pBuffer = NULL;
		}
		packBuffer.m_nLength = 0;

		//length
		if(!Read(packBuffer.m_nLength))
		{
			bResult = false;
		}

		//data
		if(bResult && m_nCurrentPos + packBuffer.m_nLength > m_nBufferLen){
			bResult = false;
		}

		if(bResult)
		{
			packBuffer.m_pBuffer = new BYTE[packBuffer.m_nLength];
			memcpy_s(packBuffer.m_pBuffer, packBuffer.m_nLength, m_pBuffer + m_nCurrentPos, packBuffer.m_nLength);
			m_nCurrentPos += packBuffer.m_nLength;
		}

		return bResult;
	}

	template<typename T>
	inline bool CDepacker::Read(PackBufferArrayOut<T>& pbao)
	{
		bool bResult = true;

		assert(pbao.m_pArray);

		//length
		//size_t sizeRead = 0;
		//if(!Read(sizeRead))
		//{
		//	bResult = false;
		//}

		//	assert(pbao.m_uSize == sizeRead);
		assert(pbao.m_uSize > 0);

		//data
		if(bResult && m_nCurrentPos + pbao.m_uSize * CPackBuffer::GetTypeSize(pbao.m_pArray[0]) > m_nBufferLen){
			bResult = false;
		}
		for(size_t i = 0; i<pbao.m_uSize; i++)
		{
			if(!Read(pbao.m_pArray[i]))
				return false;
		}
		return true;
	}

	template <typename P1, typename P2> 
	inline bool CDepacker::Read(P1& v1, P2& v2) 
	{
		bool ret = true;
		if(!Read(v1))
		{
			ret = false;
		}
		return ret && Read(v2);
	}

	template <typename P1, typename P2, typename P3> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3) 
	{
		bool ret = true;
		if(!Read(v1, v2))
		{
			ret = false;
		}

		return ret && Read(v3);
	}

	template <typename P1, typename P2, typename P3, typename P4> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3))
		{
			ret = false;
		}

		return ret && Read(v4);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4))
		{
			ret = false;
		}

		return ret && Read(v5);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4, v5))
		{
			ret = false;
		}

		return ret && Read(v6);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4, v5, v6))
		{
			ret = false;
		}

		return ret && Read(v7);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4, v5, v6, v7))
		{
			ret = false;
		}

		return ret && Read(v8);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8, P9& v9) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4, v5, v6, v7, v8))
		{
			ret = false;
		}

		return ret && Read(v9);
	}

	template <typename P1, typename P2, typename P3, typename P4, typename P5, typename P6, typename P7, typename P8, typename P9, typename P10> 
	inline bool CDepacker::Read(P1& v1, P2& v2, P3& v3, P4& v4, P5& v5, P6& v6, P7& v7, P8& v8, P9& v9, P10& v10) 
	{
		bool ret = true;
		if(!Read(v1, v2, v3, v4, v5, v6, v7, v8, v9))
		{
			ret = false;
		}

		return ret && Read(v10);
	}

}  //end of namespace PackIt

#endif	//end of #define PACKBUFFER_H